package org.dromara.fms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.utils.DateUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.common.web.core.BaseService;
import org.dromara.fms.domain.vo.FmsBillTypeVo;
import org.springframework.stereotype.Service;
import org.dromara.fms.domain.FmsBill;
import org.dromara.fms.mapper.FmsBillMapper;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Collection;

/**
 * 财务单据Service业务层处理
 */
@RequiredArgsConstructor
@Service
public class FmsBillServiceImpl extends BaseService {

    private final FmsBillMapper baseMapper;

    private final FmsAccountServiceImpl fmsAccountService;

    private final FmsBillTypeServiceImpl fmsBillTypeService;
    /**
     * 查询财务单据
     */
    public FmsBill queryById(Long id){
      return baseMapper.selectById(id);
    }

    /**
     * 查询财务单据列表
     */
    public TableDataInfo<FmsBill> queryPageList(FmsBill bo, PageQuery pageQuery) {
//        LambdaQueryWrapper<FmsBill> lqw = buildQueryWrapper(bo);
        Page<FmsBill> result = baseMapper.queryPageList(pageQuery.build(), bo);
        return TableDataInfo.build(result);
    }

    /**
     * 查询财务单据列表
     */
    public List<FmsBill> queryList(FmsBill bo) {
        return baseMapper.queryPageList(bo);
    }

  public List<FmsBill> queryListBySourceId(Long sourceId) {
    return baseMapper.selectList(this.buildQueryWrapperBySourceId(sourceId));
  }

  private LambdaQueryWrapper<FmsBill> buildQueryWrapperBySourceId(Long sourceId) {
    LambdaQueryWrapper<FmsBill> lqw = Wrappers.lambdaQuery();
    lqw.eq(sourceId != null, FmsBill::getSourceId, sourceId);
    return lqw;
  }

    private LambdaQueryWrapper<FmsBill> buildQueryWrapper(FmsBill bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<FmsBill> lqw = Wrappers.lambdaQuery();
        lqw.eq(bo.getId() != null, FmsBill::getId, bo.getId());
        lqw.eq(bo.getSourceId() != null, FmsBill::getSourceId, bo.getSourceId());
        lqw.eq(bo.getTypeId() != null, FmsBill::getTypeId, bo.getTypeId());
        lqw.like(StringUtils.isNotBlank(bo.getName()), FmsBill::getName, bo.getName());
        lqw.eq(bo.getPartnerId() != null, FmsBill::getPartnerId, bo.getPartnerId());
        lqw.between(params.get("beginPlanTime") != null && params.get("endPlanTime") != null,
            FmsBill::getPlanTime ,params.get("beginPlanTime"), params.get("endPlanTime"));
        lqw.between(params.get("beginExecutionTime") != null && params.get("endExecutionTime") != null,
            FmsBill::getExecutionTime ,params.get("beginExecutionTime"), params.get("endExecutionTime"));
        lqw.orderBy(true,true, FmsBill::getPlanTime);
        return lqw;
    }

    /**
     * 新增财务单据
     */
    public Boolean insertByBo(FmsBill bo) {
        validEntityBeforeSave(bo);
        boolean flag = baseMapper.insert(bo) > 0;
        return flag;
    }

    /**
     * 修改财务单据
     */
    public Boolean updateByBo(FmsBill bo) {
        validEntityBeforeSave(bo);
        return baseMapper.updateById(bo) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(FmsBill e){
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除财务单据
     */
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

  /**
   * 执行财务单据
   */
  @Transactional(propagation = Propagation.REQUIRED)
  public R<Void> execBill(FmsBill bill) {
    FmsBill vo = baseMapper.selectById(bill.getId());
    // 账户Id
    Long accountId = vo.getAccountId();
    // 实收金额
    double money = vo.getCostMoney();
    R<Void> res;
    // 单据状态校验
    if (!vo.getStatus().equals("1")) {
      return R.fail("单据状态必须是：新开！");
    }
    // 根据单据类型判断金额加减操作
    FmsBillTypeVo fmsBillTypeVo = fmsBillTypeService.queryById(bill.getTypeId());
    if (fmsBillTypeVo.getFmsType().equals("out")) {
      money = money * -1;
    }
    // 更新账户余额
    res = fmsAccountService.updateBalance(accountId, money);
    if (res.getCode() == R.FAIL) {
      return res;
    }
    vo.setMoney(money);
    // 更新单据状态
    vo.setStatus("0");
//    FmsBill update = MapstructUtils.convert(vo, FmsBill.class);
    validEntityBeforeSave(vo);
    res = toAjax(baseMapper.updateById(vo));
    return res;
  }

  /**
   * 冲销财务单据
   */
  @Transactional(propagation = Propagation.REQUIRED)
  public R<Void> backByBill(FmsBill bill) {
    R<Void> res;
    // 新增冲销单据
    FmsBill backBill = new FmsBill();
    BeanUtil.copyProperties(bill, backBill);
    backBill.setId(null);
    backBill.setMoney(bill.getMoney() * -1);
    backBill.setStatus("1");
    baseMapper.insert(backBill);
    // 原单据状态更新
    bill.setStatus("8");
    res = toAjax(baseMapper.updateById(bill));
    return res;
  }

  /**
   * 财务单据分期
   */
  @Transactional(propagation = Propagation.REQUIRED)
  public R<Void> divide(FmsBill bill) {
    //分期前部分金额
    Double costMoneyBefore = bill.getParamsToDouble("costMoneyBefore");
    //分期前部分日期
    int dataSelect = bill.getParamsToInt("dataSelect");
    // 分期数
    int divisor = bill.getParamsToInt("periods");
    // 总金额
    Double money = bill.getCostMoney();
    // 计算余数
    Double remain = money % divisor;
    // 平均每期金额
    Double divideMoney = (money - remain) / divisor;
    // 最后一期金额
    Double lastMonthMoney = divideMoney + remain;
    // 计划时间
    Date planTime = bill.getPlanTime();

    if (costMoneyBefore != 0 && dataSelect != 0){
      //前部分金额
      divideMoney = costMoneyBefore;
      lastMonthMoney = costMoneyBefore;
      planTime = insertBill(bill, dataSelect, divideMoney, planTime, lastMonthMoney, 1);

      //后部分金额
      money -= (costMoneyBefore * dataSelect);
      // 计算余数
      remain = money % (divisor - dataSelect);
      // 平均每期金额
      divideMoney = (money - remain) / (divisor - dataSelect);
      // 最后一期金额
      lastMonthMoney = divideMoney + remain;
      insertBill(bill, divisor, divideMoney, planTime, lastMonthMoney, dataSelect + 1);
    }else{
      insertBill(bill, divisor, divideMoney, planTime, lastMonthMoney, 1);
    }
    return R.ok();
  }

  private Date insertBill(FmsBill bill, int divisor, Double divideMoney, Date planTime, Double lastMonthMoney, int startI){
    for (int i = startI; i <= divisor; i++) {
      FmsBill divideBill = new FmsBill();
      BeanUtil.copyProperties(bill, divideBill);
      // 设置分期单据名称
      if (bill.getName() != null) {
        divideBill.setName(bill.getName() + "-分期-" + i);
      } else {
        divideBill.setName(bill.getId() + "-分期-" + i);
      }
      // 设置分期单据实收金额
      divideBill.setCostMoney(divideMoney);
      // 第一期更新单据
      if (i == 1) {
        baseMapper.updateById(divideBill);
      } else {
        // 非第一期新增单据
        // 设置分期单据id和计划时间
        divideBill.setId(null);
        planTime = DateUtils.addMonths(planTime, 1);
        divideBill.setPlanTime(planTime);
        if (i == divisor) {
          // 设置最后一期单据实收金额
          divideBill.setCostMoney(lastMonthMoney);
        }
        baseMapper.insert(divideBill);
      }
    }
    return planTime;
  }
}
